﻿using GalaSoft.MvvmLight;
using DataInterpolator.Model;
using OxyPlot.Axes;
using OxyPlot.Annotations;
using OxyPlot;
using OxyPlot.Series;
using System;
using System.Collections.Generic;
using GalaSoft.MvvmLight.Command;
using Microsoft.Win32;
using System.ComponentModel;
using System.Windows;
using GalaSoft.MvvmLight.Messaging;
using DataInterpolator.ViewModel.Messages;
using Common.Utility;
using System.Windows.Data;
using System.Windows.Media;
using Common.ViewModel;
using DataInterpolator.View;
using DataInterpolator.Properties;

namespace DataInterpolator.ViewModel
{
   /// <summary>
   /// This class contains properties that the main View can data bind to.
   /// <para>
   /// See http://www.mvvmlight.net
   /// </para>
   /// </summary>
   public class MainViewModel : ViewModelBase
   {
      private readonly IDataService _DataService;

      //private LinearAxis _TopLeft = null;
      //private LinearAxis _BotLeft = null;
      //private LinearAxis _TopMiddle = null;
      //private LinearAxis _BotMiddle = null;
      //private LinearAxis _TopRight = null;
      //private LinearAxis _BotRight = null;

      private List<InterpPairVMO> _InterpPairs = new List<InterpPairVMO>();

      /// <summary>
      /// Initializes a new instance of the MainViewModel class.
      /// </summary>
      public MainViewModel(IDataService dataService)
      {
         _DataService = dataService;

         //Set all the defaults from the setting
         _DataService.SetZeroRef(Settings.Default.ZeroReference);
         _DataService.SetTopFormation(Settings.Default.TopFormationTitle);
         _DataService.SetBotFormation(Settings.Default.BotFormationTitle);
         _DataService.SetWindow(Settings.Default.WindowStart, Settings.Default.WindowEnd);

         //Listen to added components that need to get any existing data
         Messenger.Default.Register<RequestDataMessage>(this, ComponentRequestData);

         _DataService.WellDataUpdated += _DataService_WellDataUpdated;

         //Last thing is to set the default database file
         _DataService.LoadDatabaseFile(Settings.Default.PrevDatabaseFile);

         _DataService.StatusMessage += (message) =>
         {
            MessageBox.Show(message);
         };

         //Show the database window if that was the last setting
         if (Settings.Default.DatabaseWindowVisible)
         {
            //Temporarily set to false so it will open
            Settings.Default.DatabaseWindowVisible = false;

            ShowDatabaseViewerCommand.Execute(null);
         }
      }

      private void _DataService_WellDataUpdated(PartialUpdateTypes updateType)
      {
         switch (updateType)
         {
            case PartialUpdateTypes.Full:
               WellsFullUpdate();
               break;
            default:
               WellDataView.Refresh();
               Messenger.Default.Send(new PartialUpdateMessage(updateType));
               break;
         }
      }

      private void WellsFullUpdate()
      {
         MultiSelectBindingList<WellVMO> newList = new MultiSelectBindingList<WellVMO>();

         _DataService.GetWellData((IList<Well> w) =>
         {
            if (w == null)
               return;

            for (int i = 0; i < w.Count; i++)
            {
               OxyColor indexColor = DefaultPalettes.Color[i % DefaultPalettes.Color.Count];

               newList.Add(new WellVMO(w[i]) { Color = Color.FromArgb(indexColor.A, indexColor.R, indexColor.G, indexColor.B) });
            }

         });

         WellData = newList;

         WellDataView.Refresh();

         RefreshFormationOptions();

         Messenger.Default.Send(new FullRefreshMessage(WellDataView));
      }

      private void ComponentRequestData(RequestDataMessage request)
      {
         if (WellDataView == null)
            return;

         request.RequestData(new FullRefreshMessage(WellDataView));
      }

      private MultiSelectBindingList<WellVMO> _WellData = null;

      public MultiSelectBindingList<WellVMO> WellData
      {
         get
         {
            return _WellData;
         }
         set
         {
            //Need to move the event handlers
            if (_WellData != null)
            {
               _WellData.SelectedItems.ListChanged -= _WellData_SelectedItems_ListChanged;
               _WellData.ListChanged -= OnItemChanged;
            }

            _WellData = value;

            if (_WellData != null)
            {
               _WellData.SelectedItems.ListChanged += _WellData_SelectedItems_ListChanged;
               _WellData.ListChanged += OnItemChanged;
               _WellDataView = new ListCollectionView(_WellData);
               _WellDataView.CurrentChanging += _WellDataView_CurrentChanging;
            }
         }
      }

      private ListCollectionView _WellDataView = null;

      public ListCollectionView WellDataView
      {
         get
         {
            return _WellDataView;
         }
      }

      /// <summary>
      /// The <see cref="ZeroReferenceChoice" /> property's name.
      /// </summary>
      public const string ZeroReferenceChoicePropertyName = "ZeroReferenceChoice";

      /// <summary>
      /// Sets and gets the ZeroReferenceChoice property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public string ZeroReferenceChoice
      {
         get
         {
            return Settings.Default.ZeroReference;
         }

         set
         {
            if (Settings.Default.ZeroReference == value)
            {
               return;
            }

            //Check to make sure that this value is acceptable
            if (_DataService.SetZeroRef(value))
            {
               Settings.Default.ZeroReference = value;
            }

            RaisePropertyChanged(ZeroReferenceChoicePropertyName);
         }
      }

      /// <summary>
      /// The <see cref="ZeroReferenceOptions" /> property's name.
      /// </summary>
      public const string ZeroReferenceOptionsPropertyName = "ZeroReferenceOptions";

      private BindingList<string> _ZeroReferenceOptions = new BindingList<string>();

      /// <summary>
      /// Sets and gets the ZeroReferenceOptions property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public BindingList<string> ZeroReferenceOptions
      {
         get
         {
            return _ZeroReferenceOptions;
         }

         set
         {
            if (_ZeroReferenceOptions == value)
            {
               return;
            }

            _ZeroReferenceOptions = value;
            RaisePropertyChanged(ZeroReferenceOptionsPropertyName);
         }
      }

      /// <summary>
          /// The <see cref="WindowOptions" /> property's name.
          /// </summary>
      public const string WindowOptionsPropertyName = "WindowOptions";

      private BindingList<string> _WindowOptions = new BindingList<string>();

      /// <summary>
      /// Sets and gets the WindowOptions property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public BindingList<string> WindowOptions
      {
         get
         {
            return _WindowOptions;
         }

         set
         {
            if (_WindowOptions == value)
            {
               return;
            }

            _WindowOptions = value;
            RaisePropertyChanged(WindowOptionsPropertyName);
         }
      }

      /// <summary>
          /// The <see cref="WindowStartChoice" /> property's name.
          /// </summary>
      public const string WindowStartChoicePropertyName = "WindowStartChoice";

      private string _WindowStartChoice = "KB";

      /// <summary>
      /// Sets and gets the WindowStartChoice property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public string WindowStartChoice
      {
         get
         {
            return _WindowStartChoice;
         }

         set
         {
            if (_WindowStartChoice == value)
            {
               return;
            }

            _WindowStartChoice = value;
            RaisePropertyChanged(WindowStartChoicePropertyName);

            WindowStartEnabled = _WindowStartChoice == "Custom";
         }
      }

      /// <summary>
          /// The <see cref="WindowStartEnabled" /> property's name.
          /// </summary>
      public const string WindowStartEnabledPropertyName = "WindowStartEnabled";

      private bool _WindowStartEnabled = true;

      /// <summary>
      /// Sets and gets the WindownStartEnabled property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public bool WindowStartEnabled
      {
         get
         {
            return _WindowStartEnabled;
         }

         set
         {
            if (_WindowStartEnabled == value)
            {
               return;
            }

            _WindowStartEnabled = value;
            RaisePropertyChanged(WindowStartEnabledPropertyName);
         }
      }

      /// <summary>
      /// The <see cref="WindowStart" /> property's name.
      /// </summary>
      public const string WindowStartPropertyName = "WindowStart";

      /// <summary>
      /// Sets and gets the WindowStart property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public double WindowStart
      {
         get
         {
            return Settings.Default.WindowStart;
         }

         set
         {
            if (Settings.Default.WindowStart == value)
            {
               return;
            }

            if (_DataService.SetWindow(value, Settings.Default.WindowEnd))
            {
               Settings.Default.WindowStart = value;
            }

            RaisePropertyChanged(WindowStartPropertyName);
         }
      }

      /// <summary>
          /// The <see cref="WindowEndChoice" /> property's name.
          /// </summary>
      public const string WindowEndChoicePropertyName = "WindowEndChoice";

      private string _WindowEndChoice = "";

      /// <summary>
      /// Sets and gets the WindowEndChoice property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public string WindowEndChoice
      {
         get
         {
            return _WindowEndChoice;
         }

         set
         {
            if (_WindowEndChoice == value)
            {
               return;
            }

            _WindowEndChoice = value;
            RaisePropertyChanged(WindowEndChoicePropertyName);

            WindowEndEnabled = _WindowEndChoice == "Custom";
         }
      }

      /// <summary>
          /// The <see cref="WindowEndEnabled" /> property's name.
          /// </summary>
      public const string WindowEndEnabledPropertyName = "WindowEndEnabled";

      private bool _WindowEndEnabled = true;

      /// <summary>
      /// Sets and gets the WindowEndEnabled property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public bool WindowEndEnabled
      {
         get
         {
            return _WindowEndEnabled;
         }

         set
         {
            if (_WindowEndEnabled == value)
            {
               return;
            }

            _WindowEndEnabled = value;
            RaisePropertyChanged(WindowEndEnabledPropertyName);
         }
      }

      /// <summary>
      /// The <see cref="WindowEnd" /> property's name.
      /// </summary>
      public const string WindowEndPropertyName = "WindowEnd";

      /// <summary>
      /// Sets and gets the WindowEnd property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public double WindowEnd
      {
         get
         {
            return Settings.Default.WindowEnd;
         }

         set
         {
            if (Settings.Default.WindowEnd == value)
            {
               return;
            }

            if (_DataService.SetWindow(Settings.Default.WindowStart, value))
            {
               Settings.Default.WindowEnd = value;
            }

            RaisePropertyChanged(WindowEndPropertyName);
         }
      }
      /// <summary>
      /// The <see cref="XAxisStart" /> property's name.
      /// </summary>
      public const string XAxisStartPropertyName = "XAxisStart";

      /// <summary>
      /// Sets and gets the XAxisStart property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public double XAxisStart
      {
         get
         {
            return Settings.Default.XAxisStart;
         }

         set
         {
            if (Settings.Default.XAxisStart == value)
            {
               return;
            }

            Settings.Default.XAxisStart = value;
            RaisePropertyChanged(XAxisStartPropertyName);
         }
      }
      /// <summary>
      /// The <see cref="XAxisEnd" /> property's name.
      /// </summary>
      public const string XAxisEndPropertyName = "XAxisEnd";

      /// <summary>
      /// Sets and gets the XAxisEnd property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      public double XAxisEnd
      {
         get
         {
            return Settings.Default.XAxisEnd;
         }

         set
         {
            if (Settings.Default.XAxisEnd == value)
            {
               return;
            }

            Settings.Default.XAxisEnd = value;
            RaisePropertyChanged(XAxisEndPropertyName);
         }
      }

      private void OnItemChanged(object sender, ListChangedEventArgs e)
      {
         if (e.PropertyDescriptor.Name == WellVMO.VisiblePropertyName)
         {
            RefreshFormationOptions();
         }

         //Now update the data service values
         if (e.ListChangedType == ListChangedType.ItemChanged)
         {
            _DataService.UpdateWell(_WellData[e.OldIndex].ID, (w) =>
            {
               var prop = typeof(Well).GetProperty(e.PropertyDescriptor.Name, e.PropertyDescriptor.PropertyType);

               if (prop != null)
               {
                  prop.SetValue(w, e.PropertyDescriptor.GetValue(_WellData[e.OldIndex]));
                  return true;
               }

               return false;
            });
         }
      }

      void _WellData_SelectedItems_ListChanged(object sender, ListChangedEventArgs e)
      {
         if (e.ListChangedType != ListChangedType.Reset)
            return;

         BindingList<WellVMO> selected = (sender as BindingList<WellVMO>);

         int visibleSelected = 0;

         foreach (var well in selected)
         {
            if (well.Visible)
            {
               visibleSelected++;
            }
         }

         bool isMulti = visibleSelected > 1;

         foreach (var well in WellData)
         {
            if (well.Visible && selected.Contains(well))
            {
               if (isMulti)
               {
                  well.Focus = FocusLevels.Normal;
               }
               else
               {
                  well.Focus = FocusLevels.High;
               }
            }
            else
            {
               if (isMulti)
               {
                  well.Focus = FocusLevels.Low;
               }
               else
               {
                  well.Focus = FocusLevels.Normal;
               }
            }
         }

         if (e.ListChangedType == ListChangedType.ItemChanged && e.PropertyDescriptor.Name == WellVMO.VisiblePropertyName)
         {
            RefreshFormationOptions();
         }
      }

      private void RefreshFormationOptions()
      {
         List<string> allOptions = new List<string>();

         foreach (var well in _WellData)
         {
            if (!well.Visible)
               continue;

            foreach (var formation in well.TopsData.Formations)
            {
               if (!allOptions.Contains(formation.Name))
               {
                  allOptions.Add(formation.Name);
               }
            }
         }

         List<string> finalOptions = new List<string>();

         finalOptions.Add("SeismicDatum");
         finalOptions.Add("KB");
         finalOptions.Add("LogStart");

         List<string> finalWindowOptions = new List<string>();

         finalWindowOptions.Add("Custom");
         finalWindowOptions.Add("MinValue");
         finalWindowOptions.Add("SeismicDatum");
         finalWindowOptions.Add("KB");
         finalWindowOptions.Add("LogStart");

         for (int i = 0; i < allOptions.Count; i++)
         {
            bool isMissing = false;

            foreach (var well in _WellData)
            {
               if (!well.Visible)
                  continue;

               if (!well.TopsData.Contains(allOptions[i]))
               {
                  isMissing = true;
                  break;
               }
            }

            if (!isMissing)
            {
               finalOptions.Add(allOptions[i]);
               finalWindowOptions.Add(allOptions[i]);
            }
         }

         finalWindowOptions.Add("MaxValue");

         string oldZeroRef = ZeroReferenceChoice;

         ZeroReferenceOptions = new BindingList<string>(finalOptions);

         if (!ZeroReferenceOptions.Contains(oldZeroRef))
         {
            ZeroReferenceChoice = ZeroReferenceOptions[2];
         }

         string oldWinStart = WindowStartChoice;
         string oldWinEnd = WindowEndChoice;

         WindowOptions = new BindingList<string>(finalWindowOptions);

         if (!WindowOptions.Contains(oldWinStart))
         {
            WindowStartChoice = WindowOptions[0]; //Guarenteed to always have 1 value in here
         }

         if (!WindowOptions.Contains(oldWinEnd))
         {
            WindowEndChoice = WindowOptions[0]; //Guarenteed to always have 1 value in here
         }
      }

      void _WellDataView_CurrentChanging(object sender, CurrentChangingEventArgs e)
      {
         if (e.IsCancelable)
            e.Cancel = true;
      }

      private RelayCommand _ShowDatabaseViewerCommand;

      /// <summary>
      /// Gets the ShowDatabaseViewerCommand.
      /// </summary>
      public RelayCommand ShowDatabaseViewerCommand
      {
         get
         {
            return _ShowDatabaseViewerCommand
                ?? (_ShowDatabaseViewerCommand = new RelayCommand(
                () =>
                {
                   if (!Settings.Default.DatabaseWindowVisible)
                   {
                      var database = new DatabaseWindow();
                      database.Show();
                   }
                }));
         }
      }

      private RelayCommand _ShowExportInterpolationCommand;

      /// <summary>
      /// Gets the ShowDatabaseViewerCommand.
      /// </summary>
      public RelayCommand ShowExportInterpolationCommand
      {
         get
         {
            return _ShowExportInterpolationCommand
                ?? (_ShowExportInterpolationCommand = new RelayCommand(
                () =>
                {
                   var exportWindow = new ExportInterpolationWindow();
                   exportWindow.ShowDialog();
                }));
         }
      }

      private RelayCommand _ExitCommand;

      /// <summary>
      /// Gets the MyCommand.
      /// </summary>
      public RelayCommand ExitCommand
      {
         get
         {
            return _ExitCommand
                ?? (_ExitCommand = new RelayCommand(
                () =>
                {
                   if (_DataService.UnsavedChanges())
                   {
                      var result = MessageBox.Show("You have unsaved changes in the database. Would you like to save the database before exiting?", "Unsaved Changes", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);

                      if (result == MessageBoxResult.Yes)
                      {
                         _DataService.SaveDatabaseFile(false);
                      }
                      else if (result == MessageBoxResult.No)
                      {

                      }
                      else if (result == MessageBoxResult.Cancel)
                      {
                         return;
                      }
                   }

                   Application.Current.Shutdown();
                }));
         }
      }

      private RelayCommand<CancelEventArgs> _ClosingCommand = null;

      public RelayCommand<CancelEventArgs> ClosingCommand
      {
         get
         {
            return _ClosingCommand
                ?? (_ClosingCommand = new RelayCommand<CancelEventArgs>(
                (args) =>
                {
                   ExitCommand.Execute(null);
                }));
         }
      }

      private RelayCommand _RefreshPlotCommand;

      /// <summary>
      /// Gets the RefreshPlotCommand.
      /// </summary>
      public RelayCommand RefreshPlotCommand
      {
         get
         {
            return _RefreshPlotCommand
                ?? (_RefreshPlotCommand = new RelayCommand(
                () =>
                {
                   Messenger.Default.Send(new MenuCommandMessage(MenuCommandTypes.RefreshPlot));
                }));
         }
      }

      private RelayCommand _UndoCommand;

      /// <summary>
      /// Gets the UndoCommand.
      /// </summary>
      public RelayCommand UndoCommand
      {
         get
         {
            return _UndoCommand
                ?? (_UndoCommand = new RelayCommand(
                () =>
                {
                   Messenger.Default.Send(new MenuCommandMessage(MenuCommandTypes.Undo));
                }));
         }
      }

      private RelayCommand _RedoCommand;

      /// <summary>
      /// Gets the RedoCommand.
      /// </summary>
      public RelayCommand RedoCommand
      {
         get
         {
            return _RedoCommand
                ?? (_RedoCommand = new RelayCommand(
                () =>
                {
                   Messenger.Default.Send(new MenuCommandMessage(MenuCommandTypes.Redo));
                }));
         }
      }
   }
}